
#import "SavingsCalculator.h"
#import "JSONKit.h"
#import "Data.h"
#import "lampmeta.h"

#define kLampTypes @"types"

#define kCalculationHours @"calculation_hours"
#define kEnergyPriceKWh @"energy_price_kwh"

#define kLampName @"name"
#define kLampMaxLumen @"max_lumen"
#define kLampLifeExpectancy @"life_expectancy"
#define kLampStandardPrices @"standard_prices"
#define kLampPowerNeedRelativeClassic @"power_need_relative_classic"
#define kLampReplaceWith @"replace_with"

//#warning remove these before production
#define CalcLog(fmt, arg) (void)0 //NSLog(fmt, arg)
#define CalcLog2(fmt, arg1, arg2) (void)0 //NSLog(fmt, arg1, arg2)


@implementation SavingsCalculator

-(id)initWithLampItem:(NSDictionary *)lampItem effect:(CGFloat)effect
{
    self = [super init];
    if (self) { 
        _lampItem = lampItem;
        _effect = effect;

        Data *data = [Data sharedInstance];

        // Fetch calculation data
        NSString *path = [[NSBundle mainBundle] pathForResource:@"savingscalculatordata" ofType:@"json"];
        NSString *json = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
        NSDictionary *calculationData = [json objectFromJSONString];
        NSDictionary *calculationLampTypes = calculationData[kLampTypes];
        _calculationHours = [calculationData[kCalculationHours] integerValue];
        _energyPriceKWh = [calculationData[kEnergyPriceKWh] floatValue];
                
        // Get parameters
        NSAssert1(calculationLampTypes[_lampItem[kLampItemType]] != nil, @"Type is not supported: %@", _lampItem[kLampItemType]);
        NSDictionary *calculationLampType = calculationLampTypes[_lampItem[kLampItemType]];
        CGFloat lifeExpectancy = [calculationLampType[kLampLifeExpectancy] floatValue];

        // Get Lumen
        NSDictionary *wattToLumenTable = [data wattToLumenTableForLampType:_lampItem[kLampItemType]];
        NSString *effectKey = [NSString stringWithFormat:@"%.0f", effect];
        NSArray *lumenRange = wattToLumenTable[effectKey];
        _lumen = [lumenRange[1] integerValue]; // Use the lumen max value for calculation

        // Get price
        CGFloat standardPrice = [self getStandardPriceForLampType:calculationLampType andLumen:_lumen];

        CalcLog(@"Old lamp - Type: %@", _lampItem);
        CalcLog(@"Old lamp - Calculation Type: %@", calculationLampType);
        CalcLog(@"Old lamp - Effect: %.2f", _effect);
        CalcLog(@"Old lamp - Price: %.2f", standardPrice);

        CGFloat powerNeed = [calculationLampType[kLampPowerNeedRelativeClassic] floatValue];
        CalcLog(@"Old lamp - powerNeed: %.2f (relative classic)", powerNeed);
        
        // Do calculation
        _lampsNeeded = _calculationHours / lifeExpectancy;
        _lampsPurchaseCost = _lampsNeeded * standardPrice;
        CalcLog(@"Old lamp - lifeExpectancy: %.2f", lifeExpectancy);
        CalcLog(@"Old lamp - lampsNeeded: %.2f", _lampsNeeded);
        CalcLog(@"Old lamp - lampsPurchaseCost: %.2f", _lampsPurchaseCost);
        
        _usedEnergy = _effect / 1000 * _calculationHours;
        _usedEnergyCost = _usedEnergy * _energyPriceKWh;
        CalcLog2(@"Old lamp - Used energ and cost: %.2f kWh, %.2f sek", _usedEnergy, _usedEnergyCost);
        
        // The energy need is relative to a classic lamp, if this is another 
        // type the used energy need has to be converted to a classic's level
        _usedEnergyNormalized = _usedEnergy / powerNeed;
        CalcLog(@"Calulation - Used energy: %.2f (if classic lamp was used)", _usedEnergyNormalized);
        
        // Find substitutes
        NSArray *validSubstitutes = calculationLampType[kLampReplaceWith];
        NSSet *substituteKeys = [calculationLampTypes keysOfEntriesPassingTest:^BOOL(NSString *key, NSDictionary *item, BOOL *stop) {
            return [validSubstitutes containsObject:key] && _lumen <= [item[kLampMaxLumen] integerValue];
        }];
        _substitutes = [calculationLampTypes objectsForKeys:[substituteKeys allObjects] notFoundMarker:@""];
        _substitutes = [_substitutes sortedArrayUsingDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@"energy_rank" ascending:YES]]];
                
        _substPrice = [NSMutableArray array];
        _substSaving = [NSMutableArray array];
        for (NSDictionary *type in _substitutes) {
            CGFloat price = [self loadSavedPurchasePriceForLampType:type andLumen:_lumen];
            if (!price) {
                price = [self getStandardPriceForLampType:type andLumen:_lumen];
            }
            [_substPrice addObject:@(price)];
            CGFloat saving = [self calulateSavingForLampType:type withPurchasePrice:price];
            [_substSaving addObject:@(saving)];
        }
    }
    return self;    
}

- (CGFloat) getStandardPriceForLampType:(NSDictionary *)lampType andLumen:(NSInteger)lumen
{
    NSArray *standardPrices = lampType[kLampStandardPrices]; // price and "price valid up to x lumen" pairs
    CGFloat standardPrice = [standardPrices[0] floatValue];
    for (NSInteger i = 0; i < standardPrices.count; i += 2) {
        standardPrice = [standardPrices[i] floatValue];
        NSInteger validUpToLumen = [standardPrices[i + 1] integerValue];
        if (lumen <= validUpToLumen) {
            break;
        }
    }
    return standardPrice;
}

-(void)setPurchasePrice:(float)price forSubstituteAtIndex:(NSInteger)index
{
    self.substPrice[index] = @(price);
    NSDictionary *type = (self.substitutes)[index];
    CGFloat newSaving = [self calulateSavingForLampType:type withPurchasePrice:price];
    self.substSaving[index] = @(newSaving);
    [self savePurchasePrice:price forLampType:type andLumen:self.lumen];
}

-(float)purchasePriceForSubstituteAtIndex:(NSInteger)index
{
    float price;
    NSDictionary *type = self.substitutes[index];
    price = [self loadSavedPurchasePriceForLampType:type andLumen:self.lumen];
    if (!price) {
        price = [self.substPrice[index] floatValue];
    }
    return price;
}

-(float)savingForSubstituteAtIndex:(NSInteger)index
{
    return [self.substSaving[index] floatValue];
}

-(float)savingForSubstituteAtIndex:(NSInteger)index andPurchasePrice:(float)price
{
    return [self calulateSavingForLampType:self.substitutes[index] withPurchasePrice:price];
}

-(float)calulateSavingForLampType:(NSDictionary *)type withPurchasePrice:(float)price
{
    CalcLog(@"New lamp - type: %@", type);

    // Get parameters
    CGFloat lifeExpectancy = [type[kLampLifeExpectancy] floatValue];
    CGFloat powerNeed = [type[kLampPowerNeedRelativeClassic] floatValue];
    CalcLog(@"New lamp - PowerNeed: %.2f", powerNeed);
    CalcLog(@"New lamp - lifeExpectancy: %.2f", lifeExpectancy);
    
    // Do calculation
    CGFloat lampsNeeded = self.calculationHours / lifeExpectancy;
    CGFloat lampsPurchaseCost = lampsNeeded * price;
    CalcLog(@"New lamp - lampsNeeded: %.2f", lampsNeeded);
    CalcLog(@"New lamp - lampsPurchaseCost: %.2f", lampsPurchaseCost);

    CGFloat usedEnergy = self.usedEnergyNormalized * powerNeed;
    CGFloat usedEnergyCost = usedEnergy * self.energyPriceKWh;
    CalcLog2(@"New lamp - Used energ and cost: %.2f kWh, %.2f sek", usedEnergy, usedEnergyCost);


    CGFloat totalSaving = self.lampsPurchaseCost + self.usedEnergyCost - lampsPurchaseCost - usedEnergyCost;
    CGFloat perLampSaving = totalSaving / lampsNeeded;
    perLampSaving = perLampSaving < 0 ? 0 : perLampSaving;
    CalcLog(@"New lamp - totalSaving: %.2f", totalSaving);
    CalcLog(@"New lamp - perLampSaving: %.2f", perLampSaving);

    perLampSaving = roundf(perLampSaving / 5) * 5;
    CalcLog(@"New lamp - perLampSaving: %.2f (rounded)", perLampSaving);
    
    return perLampSaving;
}

- (NSString *)purchasePriceKeyForType:(NSDictionary *)type andLumen:(NSInteger)lumen
{
    return [NSString stringWithFormat:@"%@,%d", type[kLampName], lumen];
}

#pragma mark - User Defaults
- (void)savePurchasePrice:(float)price forLampType:(NSDictionary *)type andLumen:(NSInteger)lumen
{
    NSString *key = [self purchasePriceKeyForType:type andLumen:lumen];
    [[NSUserDefaults standardUserDefaults] setFloat:price forKey:key];
}

- (float)loadSavedPurchasePriceForLampType:(NSDictionary *)type andLumen:(NSInteger)lumen
{
    NSString *key = [self purchasePriceKeyForType:type andLumen:lumen];
    return [[NSUserDefaults standardUserDefaults] floatForKey:key];
}

@end
